﻿using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.Threading.Tasks;

namespace DotNetImage.Imaging.Filters
{
    public class Conv3x3 : FilterBase
    {
        private ConvolutionMatrix m = null;
        
        #region Public properties
        public ConvolutionMatrix ConvMatrix
        {
            get { return m; }
            set { m = value; }
        }
        #endregion

        protected override void ValidateArguments()
        {
            if (this.ConvMatrix == null)
            {
                throw new ArgumentNullException("ConvMatrix");
            }
            if (0 == m.Factor)
            {
                throw new ArgumentException("Factor must not be equal to zero");
            }
        }


        protected override void PrepareData(DotNetImage bmp)
        {
            base.PrepareData(bmp);
            bmp.CreatePixelCopy();

            // Skip 1 line of pixels around the image
            startX = 1;
            startY = 1;
        }


        protected override void ProcessPixel(ref Pixel pix, uint x = 0, uint y = 0)
        {
            Pixel pix11 = Subject.GetPreservedPixel(x - 1, y - 1);
            Pixel pix12 = Subject.GetPreservedPixel(x, y - 1);
            Pixel pix13 = Subject.GetPreservedPixel(x + 1, y - 1);
            Pixel pix21 = Subject.GetPreservedPixel(x - 1, y);
            Pixel pix23 = Subject.GetPreservedPixel(x + 1, y);
            Pixel pix31 = Subject.GetPreservedPixel(x - 1, y + 1);
            Pixel pix32 = Subject.GetPreservedPixel(x, y + 1);
            Pixel pix33 = Subject.GetPreservedPixel(x + 1, y + 1);

            pix.Red = GetColorValue(pix11.Red, pix12.Red, pix13.Red, pix21.Red, pix.Red, pix23.Red, pix31.Red, pix32.Red, pix33.Red);
            if (pix.ColorChannelCount >= 3)
            {
                pix.Green = GetColorValue(pix11.Green, pix12.Green, pix13.Green, pix21.Green, pix.Green, pix23.Green, pix31.Green, pix32.Green, pix33.Green);
                pix.Blue = GetColorValue(pix11.Blue, pix12.Blue, pix13.Blue, pix21.Blue, pix.Blue, pix23.Blue, pix31.Blue, pix32.Blue, pix33.Blue);
                if (pix.ColorChannelCount == 4)
                {
                    pix.Alpha = GetColorValue(pix11.Alpha, pix12.Alpha, pix13.Alpha, pix21.Alpha, pix.Alpha, pix23.Alpha, pix31.Alpha, pix32.Alpha, pix33.Alpha);
                }
            }
        }



        /// <summary>
        /// Multiplies bytes values by 3x3 matrix values, divides by matrix factor and adds offset.
        /// </summary>
        /// <param name="topLeft"></param>
        /// <param name="topMiddle"></param>
        /// <param name="topRight"></param>
        /// <param name="midLeft"></param>
        /// <param name="pixelCenter"></param>
        /// <param name="midRight"></param>
        /// <param name="bottomLeft"></param>
        /// <param name="bottomMid"></param>
        /// <param name="bottomRight"></param>
        /// <returns></returns>
        private uint GetColorValue(uint topLeft, uint topMiddle, uint topRight, uint midLeft, uint pixelCenter, uint midRight, uint bottomLeft, uint bottomMid, uint bottomRight)
        {
            long nPixel = ((
                (
                    (topLeft * m.TopLeft) +
                    (topMiddle * m.TopMid) +
                    (topRight * m.TopRight) +
                    (midLeft * m.MidLeft) +
                    (pixelCenter * m.Pixel) +
                    (midRight * m.MidRight) +
                    (bottomLeft * m.BottomLeft) +
                    (bottomMid * m.BottomMid) +
                    (bottomRight * m.BottomRight)
                )
                / m.Factor) + m.Offset);
            return (uint)(nPixel > Subject.ColorChannelMaxValue ? Subject.ColorChannelMaxValue : nPixel);
        }

    }
}
